home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
MBUF.C
< prev
next >
Wrap
Text File
|
1993-11-23
|
8KB
|
396 lines
/* Primitive mbuf allocate/free routines */
#include <stdio.h>
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "proc.h"
/* Free packet (a chain of mbufs). Return pointer to next packet on queue,
* if any
*/
struct mbuf *
free_p(struct mbuf *bp)
{
struct mbuf *abp = bp->anext;
if(bp == NULLBUF) {
return NULLBUF;
}
while(bp != NULLBUF) {
bp = free_mbuf(bp);
}
return abp;
}
/* Free entire queue of packets (of mbufs) */
void
free_q(struct mbuf **q)
{
struct mbuf *bp;
while((bp = dequeue(q)) != NULLBUF) {
free_p(bp);
}
}
/* Count up the total number of bytes in a packet */
int16
len_p(struct mbuf *bp)
{
int16 cnt = 0;
while(bp != NULLBUF) {
cnt += bp->cnt;
bp = bp->next;
}
return cnt;
}
/* Count up the number of packets in a queue */
int16
len_q(struct mbuf *bp)
{
int16 cnt = 0;
for( ; bp != NULLBUF; cnt++, bp = bp->anext) ;
return cnt;
}
/* Trim mbuf to specified length by lopping off end */
void
trim_mbuf(struct mbuf **bpp,int16 length)
{
int16 tot = 0;
struct mbuf *bp;
if(bpp == NULLBUFP || *bpp == NULLBUF) {
/* Nothing to trim */
return;
}
if(length == 0) {
/* Toss the whole thing */
free_p(*bpp);
*bpp = NULLBUF;
return;
}
/* Find the point at which to trim. If length is greater than
* the packet, we'll just fall through without doing anything
*/
for(bp = *bpp; bp != NULLBUF; bp = bp->next) {
if(tot + bp->cnt < length) {
tot += bp->cnt;
} else {
/* Cut here */
bp->cnt = length - tot;
free_p(bp->next);
bp->next = NULLBUF;
break;
}
}
}
/* Duplicate/enqueue/dequeue operations based on mbufs */
/* Duplicate first 'cnt' bytes of packet starting at 'offset'.
* This is done without copying data; only the headers are duplicated,
* but without data segments of their own. The pointers are set up to
* share the data segments of the original copy. The return pointer is
* passed back through the first argument, and the return value is the
* number of bytes actually duplicated.
*/
int16
dup_p(struct mbuf **hp,struct mbuf *bp,int16 offset,int16 cnt)
{
struct mbuf *cp;
int16 tot = 0;
if(cnt == 0 || bp == NULLBUF || hp == NULLBUFP) {
if(hp != NULLBUFP) {
*hp = NULLBUF;
}
return 0;
}
*hp = cp = alloc_mbuf(0);
/* Skip over leading mbufs that are smaller than the offset */
while(bp != NULLBUF && bp->cnt <= offset) {
offset -= bp->cnt;
bp = bp->next;
}
if(bp == NULLBUF) {
free_mbuf(cp);
*hp = NULLBUF;
return 0; /* Offset was too big */
}
for(;;) {
/* Make sure we get the original, "real" buffer
* (i.e. handle the case of duping a dupe)
*/
if(bp->dup != NULLBUF) {
cp->dup = bp->dup;
} else {
cp->dup = bp;
}
/* Increment the duplicated buffer's reference count */
cp->dup->refcnt++;
cp->data = bp->data + offset;
cp->cnt = min(cnt,bp->cnt - offset);
offset = 0;
cnt -= cp->cnt;
tot += cp->cnt;
bp = bp->next;
if(cnt == 0 || bp == NULLBUF) {
break;
}
cp = cp->next = alloc_mbuf(0);
}
return tot;
}
#if defined NETROM || defined PACKET
/* Copy first 'cnt' bytes of packet into a new, single mbuf */
struct mbuf *
copy_p(struct mbuf *bp,int16 cnt)
{
struct mbuf *cp;
char *wp;
if(bp == NULLBUF || cnt == 0) {
return NULLBUF;
}
cp = alloc_mbuf(cnt);
wp = cp->data;
while(cnt != 0 && bp != NULLBUF) {
int16 n = min(cnt,bp->cnt);
memcpy(wp,bp->data,n);
wp += n;
cp->cnt += n;
cnt -= n;
bp = bp->next;
}
return cp;
}
#endif
/* Copy and delete "cnt" bytes from beginning of packet. Return number of
* bytes actually pulled off
*/
int16
pullup(struct mbuf **bph,char *buf,int16 cnt)
{
int16 tot = 0;
if(bph != NULLBUFP) {
struct mbuf *bp;
while(cnt > 0 && (bp = *bph) != NULLBUF) {
int16 n = min(cnt,bp->cnt);
if(buf != NULLCHAR) {
if(n == 1) { /* Common case optimization */
*buf = *bp->data;
} else if(n > 1) {
memcpy(buf,bp->data,n);
}
buf += n;
}
tot += n;
cnt -= n;
bp->data += n;
bp->cnt -= n;
if(bp->cnt <= 0) {
/* If this is the last mbuf of a packet but there
* are others on the queue, return a pointer to
* the next on the queue. This allows pullups to
* to work on a packet queue
*/
if(bp->next == NULLBUF && bp->anext != NULLBUF) {
*bph = bp->anext;
free_mbuf(bp);
} else {
*bph = free_mbuf(bp);
}
}
}
}
return tot;
}
/* Append mbuf to end of mbuf chain */
void
append(struct mbuf **bph,struct mbuf *bp)
{
if(bph == NULLBUFP || bp == NULLBUF) {
return;
}
if(*bph == NULLBUF) {
/* First one on chain */
*bph = bp;
} else {
struct mbuf *p = *bph;
for( ; p->next != NULLBUF ; p = p->next) ;
p->next = bp;
}
}
/* Insert specified amount of contiguous new space at the beginning of an
* mbuf chain. If enough space is available in the first mbuf, no new space
* is allocated. Otherwise a mbuf of the appropriate size is allocated and
* tacked on the front of the chain.
*
* This operation is the logical inverse of pullup(), hence the name.
*/
struct mbuf *
pushdown(struct mbuf *bp,int16 size)
{
/* Check that bp is real, that it hasn't been duplicated, and
* that it itself isn't a duplicate before checking to see if
* there's enough space at its front.
*/
if(bp != NULLBUF
&& bp->refcnt == 1
&& bp->dup == NULLBUF
&& (bp->data - (char *)(bp + 1)) >= size) {
/* No need to alloc new mbuf, just adjust this one */
bp->data -= size;
bp->cnt += size;
} else {
struct mbuf *nbp = alloc_mbuf(size);
nbp->next = bp;
nbp->cnt = size;
bp = nbp;
}
return bp;
}
/* Append packet to end of packet queue */
void
enqueue(struct mbuf **q,struct mbuf *bp)
{
int i_state;
if(q == NULLBUFP || bp == NULLBUF) {
return;
}
i_state = dirps();
if(*q == NULLBUF) {
/* List is empty, stick at front */
*q = bp;
} else {
struct mbuf *p = *q;
for( ; p->anext != NULLBUF ; p = p->anext) ;
p->anext = bp;
}
restore(i_state);
psignal(q,1);
}
/* Unlink a packet from the head of the queue */
struct mbuf *
dequeue(struct mbuf **q)
{
struct mbuf *bp;
int i_state;
if(q == NULLBUFP) {
return NULLBUF;
}
i_state = dirps();
if((bp = *q) != NULLBUF) {
*q = bp->anext;
bp->anext = NULLBUF;
}
restore(i_state);
return bp;
}
/* Copy user data into an mbuf */
struct mbuf *
qdata(char *data,int16 cnt)
{
struct mbuf *bp = alloc_mbuf(cnt);
memcpy(bp->data,data,cnt);
bp->cnt = cnt;
return bp;
}
#ifdef VANESSA
/* Copy mbuf data into user buffer */
int16
dqdata(struct mbuf *bp,char *buf,unsigned cnt)
{
int16 tot = 0;
if(buf != NULLCHAR) {
struct mbuf *bp1;
for(bp1 = bp; bp1 != NULLBUF; bp1 = bp1->next) {
int16 n = min(bp1->cnt,cnt);
memcpy(buf,bp1->data,n);
cnt -= n;
buf += n;
tot += n;
}
free_p(bp);
}
return tot;
}
#endif
/* Pull a 32-bit integer in host order from buffer in network byte order.
* On error, return 0. Note that this is indistinguishable from a normal
* return.
*/
int32
pull32(struct mbuf **bpp)
{
char buf[4];
if(pullup(bpp,buf,4) != 4) {
/* Return zero if insufficient buffer */
return 0;
}
return get32(buf);
}
/* Pull a 16-bit integer in host order from buffer in network byte order.
* Return -1 on error
*/
int16
pull16(struct mbuf **bpp)
{
char buf[2];
if(pullup(bpp,buf,2) != 2){
/* Return -1 if insufficient buffer */
return -1;
}
return get16(buf);
}
/* Pull single character from mbuf
* Return -1 on error
*/
int
pullchar(struct mbuf **bpp)
{
char c;
if(pullup(bpp,&c,1) != 1) {
/* Return -1 if insufficient buffer */
return -1;
}
return c;
}